/**
* jquery.autoFocus.js
* 基于jQuery扩展插件 autoFocus
* 仅支持垂直方向 fixed
* create: 2011.12.05
* update: 2013.05.27
* admin@laoshu133.com

-- 参数说明 --
	- autoFocus {Function}
	- @Param options {Object} --总体配置{
			offset: 偏移值，实际scrollTop 和 目标的 offsetTop 的偏移值，默认 0,
			buffer: true, --是否开启缓冲,
			toggleHash: {Boolean} --动画完成后时候在地址栏显示HASH值, buffer为true时，此参数无效；默认 false
			focusClass: 获得焦点时的className； 默认 focus,
			bufferOffset: {Number} --缓冲偏移值；默认 100,
			bufferDuration: {Number} -- 动画事件；默认 320，单位 ms
		}
	- @return {jQuery}
-- 参数说明 end --
*/
;(function(global, document, $){
	var 
	view = $(global),
	noop = function(){},
	_ops = {
		offset: 0,
		buffer: true,
		toggleHash: false,
		focusClass: 'focus',
		bufferOffset: 100,
		bufferDuration: 320
	},
	checkTimer,
	checkList = [],
	scrollBound = false,
	checkScroll = function(){
		var scrollTop = view.scrollTop();
		$.each(checkList, function(i){
			var cache = groupCache[this.groupName];
			if(cache.focused || cache.scrolling){
				return;
			}

			var offsetTop = this.targetOffsetTop = this.target.offset().top;
			if(scrollTop >= offsetTop + cache.ops.offset){
				cache.focused = true;
				focusTrigger(this);
			}
		});
		$.each(groupCache, function(k, cache){
			if(!cache.scrolling && !cache.focused && cache.prevTrigger){
				cache.prevTrigger.removeClass(cache.ops.focusClass);
			}
			cache.focused = false;
		});
	},
	checkScrollHandler = function(){
		clearTimeout(checkTimer);
		checkTimer = setTimeout(checkScroll, 16);
	},
	groupCache = {},
	focusTrigger = function(trigger){
		var cache = groupCache[trigger.data('@groupName')];
		if(cache.prevTrigger && trigger[0] !== cache.prevTrigger[0]){
			cache.prevTrigger.removeClass(cache.ops.focusClass);
		}
		trigger.addClass(cache.ops.focusClass);
		cache.prevTrigger = trigger;
	},
	focusTarget = function(e){
		e && e.preventDefault();
		
		var 
		trigger = $(this),
		target = trigger.data('@target'),
		cache = groupCache[trigger.data('@groupName')],
		ops = cache.ops;

		var top = target.offset().top + ops.offset;
		if(ops.buffer){
			var 
			scrollTop = view.scrollTop(),
			fakeTop = scrollTop > top ? top + ops.bufferOffset : top - ops.bufferOffset;
			global.scrollTo(view.scrollLeft(), fakeTop);
			cache.scrolling = true;
			focusTrigger(trigger);
			scrollTo(top, ops.bufferDuration, function(){
				//if(ops.toggleHash){
				//	global.location.hash = '#' + target[0].id;
				//}
				setTimeout(function(){
					cache.scrolling = false;
					focusTrigger(trigger);
				}, 24);
			});
		}
		else{
			if(ops.toggleHash){
				global.location.hash = '#' + target[0].id;
			}
			global.scrollTo(view.scrollLeft(), top);
			focusTrigger(trigger);
		}
	},
	scrollTo = (function(){
		var 
		timer,
		fxEase = function(t){return (t*=2)<1?.5*t*t:.5*(1-(--t)*(t-2));};
		return function(top, duration, callback, left){
			duration = ~~duration || 480;
			top = Math.max(0, ~~top);
			clearTimeout(timer);
			var 
			tMark = new Date(),
			currTop = view.scrollTop(),
			height = top - currTop,
			setTop = function(top){ global.scrollTo(left || 0, top);},
			fx = function(){
				var tMap = new Date() - tMark;
				if(tMap >= duration){
					setTop(top);
					return (callback || ds.noop).call(ds, top);
				}
				setTop(currTop + height * fxEase(tMap/duration));
				timer = setTimeout(fx, 16);
			};
			fx();
		};
	})();
	$.fn.autoFocus = function(ops){
		ops = ops || {};
		for(var k in _ops){
			if(typeof ops[k] === 'undefined'){
				ops[k] = _ops[k];
			}
		}
		if(!scrollBound && this.length > 0){
			view.bind('scroll.autoFocus', checkScrollHandler);
			checkScrollHandler();
			scrollBound = true;
		}
		var groupName = this.selector;
		groupCache[groupName] = { ops: ops };
		this.each(function(){
			var 
			id = this.href.slice(this.href.lastIndexOf('#') + 1),
			target = $('#' + id);
			if(target.length > 0){
				var trigger = $(this);
				trigger.data('@groupName', (trigger.groupName = groupName));
				trigger.data('@target', (trigger.target = target));
				trigger.targetOffsetTop = target.offset().top;
				trigger.bind('click.autoFocus', focusTarget);
				trigger.removeClass(ops.focusClass);
				checkList.push(trigger);
			}
		});
		checkList.sort(function(a, b){
			return b.targetOffsetTop - a.targetOffsetTop;
		});
		return this;
	};
})(this, this.document, jQuery);